﻿/*
 ************************************
 * 
 * 描 述: 游戏核心类，负责处理游戏核心算法，与界面无关
 * Created By WZH
 * Created Time: 2022/1/26 17:21:03
 * 
 ************************************
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace _2048Test
{
    struct Location
    {
        public int x;
        public int y;
    }
    class GameMainManager
    {
        private static GameMainManager _instance;
        public static GameMainManager Instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = new GameMainManager();
                }
                return _instance;
            }
        }

        private int[,] _mapArr;
        private int[,] _originalMapArr;
        private int[] _mergeArr;
        private int[] _tempArr;
        private List<Location> _emptyArr;

        private Random _random;

        private int _col;
        private int _rol;

        /// <summary>
        /// 地图是否发生改变
        /// </summary>
        public bool IsChange { get; set; }
        /// <summary>
        /// 游戏是否结束
        /// </summary>
        public bool IsOver { get; set; }
        /// <summary>
        /// 分数
        /// </summary>
        public int Score { get; set; }
        public GameMainManager()
        {
            this._rol = this._col = 4;
            this._mapArr = new int[this._rol, this._col];
            this._originalMapArr = new int[this._rol, this._col];
            this._mergeArr = new int[this._col];
            this._tempArr = new int[this._col];
            this._emptyArr = new List<Location>();

            this._random = new Random();

            this.IsOver = false;
        }

        #region 数据合并

        /// <summary>
        /// 删除0元素
        /// </summary>
        /// <param name="arr"></param>
        private void RemoveZero()
        {
            int length = this._mergeArr.Length;
            Array.Clear(this._tempArr, 0, length);
            int index = 0;
            for (int i = 0; i < length; i++)
            {
                if (this._mergeArr[i] != 0)
                    this._tempArr[index++] = this._mergeArr[i];
            }
            this._tempArr.CopyTo(this._mergeArr, 0);
        }

        private void MergeElem()
        {
            this.RemoveZero();
            // 合并数据
            for (int i = 0; i < this._mergeArr.Length - 1; i++)
            {
                if (this._mergeArr[i] != 0 && this._mergeArr[i] == this._mergeArr[i + 1])
                {
                    this._mergeArr[i] += this._mergeArr[i + 1];
                    this.Score += this._mergeArr[i];
                    this._mergeArr[i + 1] = 0;
                }
            }
            this.RemoveZero();
        }

        #endregion

        #region 移动

        /// <summary>
        /// 上移元素
        /// </summary>
        /// <param name="arr"></param>
        private void MoveUp()
        {
            int rol = this._mapArr.GetLength(0);
            int col = this._mapArr.GetLength(1);
            Array.Clear(this._mergeArr, 0, this._rol);
            for (int i = 0; i < rol; i++)
            {
                for (int j = 0; j < col; j++)
                    this._mergeArr[j] = this._mapArr[j, i];

                this.MergeElem();

                for (int j = 0; j < col; j++)
                    this._mapArr[j, i] = this._mergeArr[j];
            }
        }

        /// <summary>
        /// 下移元素
        /// </summary>
        /// <param name="arr"></param>
        private void MoveDown()
        {
            int rol = this._mapArr.GetLength(0);
            int col = this._mapArr.GetLength(1);
            Array.Clear(this._mergeArr, 0, this._rol);
            for (int i = 0; i < rol; i++)
            {
                for (int j = col - 1; j >= 0; j--)
                    this._mergeArr[(col - 1) - j] = this._mapArr[j, i];

                this.MergeElem();

                for (int j = rol - 1; j >= 0; j--)
                    this._mapArr[j, i] = this._mergeArr[(col - 1) - j];
            }
        }

        /// <summary>
        /// 左移元素
        /// </summary>
        /// <param name="arr"></param>
        private void MoveLeft()
        {
            int rol = this._mapArr.GetLength(0);
            int col = this._mapArr.GetLength(1);
            Array.Clear(this._mergeArr, 0, this._rol);
            for (int i = 0; i < rol; i++)
            {
                for (int j = 0; j < col; j++)
                    this._mergeArr[j] = this._mapArr[i, j];

                this.MergeElem();

                for (int j = 0; j < col; j++)
                    this._mapArr[i, j] = this._mergeArr[j];
            }
        }

        private void MoveRight()
        {
            int rol = this._mapArr.GetLength(0);
            int col = this._mapArr.GetLength(1);
            Array.Clear(this._mergeArr, 0, this._rol);
            for (int i = 0; i < rol; i++)
            {
                for (int j = col - 1; j >= 0; j--)
                    this._mergeArr[(col - 1) - j] = this._mapArr[i, j];

                this.MergeElem();

                for (int j = col - 1; j >= 0; j--)
                    this._mapArr[i, j] = this._mergeArr[(col - 1) - j];
            }
        }

        public void Move(Direction dir)
        {
            Array.Copy(this._mapArr, this._originalMapArr, this._mapArr.Length);
            switch (dir)
            {
                case Direction.Up:
                    this.MoveUp();
                    break;
                case Direction.Down:
                    this.MoveDown();
                    break;
                case Direction.Left:
                    this.MoveLeft();
                    break;
                case Direction.Right:
                    this.MoveRight();
                    break;
                default:
                    break;
            }
            this.IsChange = false;
            for (int i = 0; i < this._rol; i++)
            {
                for (int j = 0; j < this._col; j++)
                {
                    if(this._mapArr[i, j] != this._originalMapArr[i,j])
                    {
                        this.IsChange = true;
                    }
                }
            }
            if (!this.IsChange)
            {
                this.CakcykateEmpty();

                if (this._emptyArr.Count < 1)
                {
                    this.IsOver = true;
                    return;
                }
                return;
            }
            this.GenerateNumber();
        }

        #endregion

        #region 生成数字

        private void CakcykateEmpty()
        {
            this._emptyArr.Clear();
            for (int i = 0; i < this._rol; i++)
            {
                for (int j = 0; j < this._col; j++)
                {
                    if (this._mapArr[i, j] == 0)
                    {
                        Location idx = new Location();
                        idx.x = i;
                        idx.y = j;
                        this._emptyArr.Add(idx);
                    }
                }
            }
        }

        public void GenerateNumber()
        {
            this.CakcykateEmpty();

            if (this._emptyArr.Count < 1)
            {
                this.IsOver = true;
                return;
            }

            int idx = this._random.Next(0, this._emptyArr.Count);
            Location vIdx = this._emptyArr[idx];
            int p = this._random.Next(0, 100);
            this._mapArr[vIdx.x, vIdx.y] = p < 90 ? 2 : 4;

            this.DrawMap();
        }

        #endregion

        public void DrawMap()
        {
            Console.Clear();
            for (int i = 0; i < this._rol; i++)
            {
                for (int j = 0; j < this._col; j++)
                {
                    Console.Write(this._mapArr[i, j] + "\t");
                }
                Console.WriteLine();
            }
        }
    }
}
